package cn.iocoder.yudao.module.iot.service.scene;

import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.quartz.core.scheduler.SchedulerManager;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobDO;
import cn.iocoder.yudao.module.infra.dal.mysql.job.JobMapper;
import cn.iocoder.yudao.module.iot.dal.dataobject.scene.IotSceneConditionDO;
import cn.iocoder.yudao.module.iot.dal.mysql.scene.IotSceneConditionMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.quartz.SchedulerException;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 场景定时任务 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Slf4j
public class IotSceneTimerServiceImpl implements IotSceneTimerService {

    @Resource
    private SchedulerManager schedulerManager;

    @Resource
    private JobMapper jobMapper;

    @Resource
    private IotSceneConditionMapper sceneConditionMapper;

    @Override
    public Long createSceneTimer(Long sceneId, IotSceneConditionDO condition) {
        return createSceneTimerInternal(sceneId, condition, true);
    }

    @Override
    public void updateSceneTimer(Long sceneId, IotSceneConditionDO condition) {
        updateSceneTimerInternal(sceneId, condition, true);
    }

    @Override
    public void deleteSceneTimer(Long sceneId) {
        deleteSceneTimerInternal(sceneId, true);
    }

    @Override
    public List<IotSceneConditionDO> getSceneTimerConditions(Long sceneId) {
        return getSceneTimerConditionsInternal(sceneId, true);
    }

    @Override
    public void syncSceneTimer(Long sceneId) {
        syncSceneTimerInternal(sceneId, true);
    }

    /**
     * 内部方法：创建场景定时任务
     * @param sceneId 场景ID
     * @param condition 定时条件
     * @param clearTenant 是否清理租户上下文
     * @return 任务ID
     */
    private Long createSceneTimerInternal(Long sceneId, IotSceneConditionDO condition, boolean clearTenant) {
        try {
            // 设置默认租户ID
            TenantContextHolder.setTenantId(1L);
            
            // 构建任务参数
            Map<String, Object> jobParam = new HashMap<>();
            jobParam.put("sceneId", sceneId);
            String paramJson = JsonUtils.toJsonString(jobParam);

            // 为每个场景创建独立的handler名称
            String handlerName = "iotSceneTimerJob";

            // 检查是否已存在该场景的定时任务
            List<JobDO> existingJobs = jobMapper.selectList(
                new LambdaQueryWrapperX<JobDO>()
                    .eq(JobDO::getHandlerName, handlerName)
            );
            
            // 检查是否已存在该场景的任务
            for (JobDO existingJob : existingJobs) {
                try {
                    Map<String, Object> existingParam = JsonUtils.parseObject(existingJob.getHandlerParam(), Map.class);
                    Long existingSceneId = Long.valueOf(existingParam.get("sceneId").toString());
                    if (existingSceneId.equals(sceneId)) {
                        log.warn("[createSceneTimer][该场景的定时任务已存在，场景ID: {}, 任务ID: {}]", sceneId, existingJob.getId());
                        return existingJob.getId();
                    }
                } catch (Exception e) {
                    log.warn("[createSceneTimer][解析现有任务参数失败，任务ID: {}]", existingJob.getId(), e);
                }
            }

            // 转换时间为Cron表达式
            String cronExpression = convertTimeToCron(condition.getValue());

            // 创建定时任务
            JobDO job = JobDO.builder()
                    .name("场景定时任务-" + sceneId)
                    .handlerName(handlerName)
                    .handlerParam(paramJson)
                    .cronExpression(cronExpression)
                    .retryCount(3)
                    .retryInterval(60)
                    .monitorTimeout(0)
                    .status(1) // 启用状态
                    .build();

            jobMapper.insert(job);

            // 添加到调度器
            schedulerManager.addJob(job.getId(), handlerName, job.getHandlerParam(),
                    job.getCronExpression(), job.getRetryCount(), job.getRetryInterval());

            log.info("[createSceneTimer][创建场景定时任务成功，场景ID: {}, 任务ID: {}, 时间: {}]", 
                    sceneId, job.getId(), condition.getValue());
            return job.getId();

        } catch (Exception e) {
            log.error("[createSceneTimer][创建场景定时任务失败，场景ID: {}, 条件: {}]", sceneId, condition, e);
            throw new RuntimeException("创建定时任务失败", e);
        } finally {
            if (clearTenant) {
                // 清理租户上下文
                TenantContextHolder.clear();
            }
        }
    }

    /**
     * 内部方法：更新场景定时任务
     * @param sceneId 场景ID
     * @param condition 定时条件
     * @param clearTenant 是否清理租户上下文
     */
    private void updateSceneTimerInternal(Long sceneId, IotSceneConditionDO condition, boolean clearTenant) {
        try {
            // 设置默认租户ID
            TenantContextHolder.setTenantId(1L);
            
            String handlerName = "iotSceneTimerJob";
            
            // 查找该场景对应的任务
            List<JobDO> existingJobs = jobMapper.selectList(
                new LambdaQueryWrapperX<JobDO>()
                    .eq(JobDO::getHandlerName, handlerName)
            );
            
            JobDO targetJob = null;
            for (JobDO existingJob : existingJobs) {
                try {
                    Map<String, Object> existingParam = JsonUtils.parseObject(existingJob.getHandlerParam(), Map.class);
                    Long existingSceneId = Long.valueOf(existingParam.get("sceneId").toString());
                    if (existingSceneId.equals(sceneId)) {
                        targetJob = existingJob;
                        break;
                    }
                } catch (Exception e) {
                    log.warn("[updateSceneTimer][解析现有任务参数失败，任务ID: {}]", existingJob.getId(), e);
                }
            }
            
            if (targetJob == null) {
                log.warn("[updateSceneTimer][该场景的定时任务不存在，创建新任务，场景ID: {}]", sceneId);
                createSceneTimerInternal(sceneId, condition, clearTenant);
                return;
            }

            // 构建任务参数
            Map<String, Object> jobParam = new HashMap<>();
            jobParam.put("sceneId", sceneId);
            String paramJson = JsonUtils.toJsonString(jobParam);

            // 转换时间为Cron表达式
            String cronExpression = convertTimeToCron(condition.getValue());

            // 更新任务
            JobDO updateJob = JobDO.builder()
                    .id(targetJob.getId())
                    .handlerParam(paramJson)
                    .cronExpression(cronExpression)
                    .build();

            jobMapper.updateById(updateJob);

            // 更新调度器
            schedulerManager.updateJob(handlerName, paramJson, cronExpression, 
                    targetJob.getRetryCount(), targetJob.getRetryInterval());

            log.info("[updateSceneTimer][更新场景定时任务成功，场景ID: {}, 任务ID: {}, 时间: {}]", 
                    sceneId, targetJob.getId(), condition.getValue());

        } catch (Exception e) {
            log.error("[updateSceneTimer][更新场景定时任务失败，场景ID: {}, 条件: {}]", sceneId, condition, e);
            throw new RuntimeException("更新定时任务失败", e);
        } finally {
            if (clearTenant) {
                // 清理租户上下文
                TenantContextHolder.clear();
            }
        }
    }

    /**
     * 内部方法：删除场景定时任务
     * @param sceneId 场景ID
     * @param clearTenant 是否清理租户上下文
     */
    private void deleteSceneTimerInternal(Long sceneId, boolean clearTenant) {
        try {
            // 设置默认租户ID
            TenantContextHolder.setTenantId(1L);
            
            String handlerName = "iotSceneTimerJob";
            
            // 查找该场景对应的任务
            List<JobDO> existingJobs = jobMapper.selectList(
                new LambdaQueryWrapperX<JobDO>()
                    .eq(JobDO::getHandlerName, handlerName)
            );
            
            for (JobDO existingJob : existingJobs) {
                try {
                    Map<String, Object> existingParam = JsonUtils.parseObject(existingJob.getHandlerParam(), Map.class);
                    Long existingSceneId = Long.valueOf(existingParam.get("sceneId").toString());
                    if (existingSceneId.equals(sceneId)) {
                        // 从调度器中删除任务
                        try {
                            schedulerManager.deleteJob(handlerName);
                        } catch (SchedulerException e) {
                            log.warn("[deleteSceneTimer][从调度器删除任务失败，任务名称: {}]", handlerName, e);
                        }

                        // 从数据库中删除任务
                        jobMapper.deleteById(existingJob.getId());
                        log.info("[deleteSceneTimer][删除场景定时任务成功，场景ID: {}, 任务ID: {}]", sceneId, existingJob.getId());
                        return;
                    }
                } catch (Exception e) {
                    log.warn("[deleteSceneTimer][解析现有任务参数失败，任务ID: {}]", existingJob.getId(), e);
                }
            }
            
            log.info("[deleteSceneTimer][场景定时任务不存在，场景ID: {}]", sceneId);

        } catch (Exception e) {
            log.error("[deleteSceneTimer][删除场景定时任务失败，场景ID: {}]", sceneId, e);
            throw new RuntimeException("删除定时任务失败", e);
        } finally {
            if (clearTenant) {
                // 清理租户上下文
                TenantContextHolder.clear();
            }
        }
    }

    /**
     * 内部方法：获取场景的定时条件
     * @param sceneId 场景ID
     * @param clearTenant 是否清理租户上下文
     * @return 定时条件列表
     */
    private List<IotSceneConditionDO> getSceneTimerConditionsInternal(Long sceneId, boolean clearTenant) {
        try {
            // 设置默认租户ID
            TenantContextHolder.setTenantId(1L);
            
            return sceneConditionMapper.selectListBySceneId(sceneId).stream()
                    .filter(condition -> "定时".equals(condition.getAttr()))
                    .collect(java.util.stream.Collectors.toList());
        } finally {
            if (clearTenant) {
                // 清理租户上下文
                TenantContextHolder.clear();
            }
        }
    }

    /**
     * 内部方法：同步场景定时任务
     * @param sceneId 场景ID
     * @param clearTenant 是否清理租户上下文
     */
    private void syncSceneTimerInternal(Long sceneId, boolean clearTenant) {
        try {
            // 设置默认租户ID
            TenantContextHolder.setTenantId(1L);
            
            // 获取场景的定时条件
            List<IotSceneConditionDO> timerConditions = getSceneTimerConditionsInternal(sceneId, false);
            
            if (timerConditions.isEmpty()) {
                log.info("[syncSceneTimer][场景无定时条件，删除现有定时任务，场景ID: {}]", sceneId);
                deleteSceneTimerInternal(sceneId, false);
                return;
            }

            // 为每个定时条件创建或更新任务
            for (IotSceneConditionDO condition : timerConditions) {
                // 检查是否已存在该场景的任务
                String handlerName = "iotSceneTimerJob";
                List<JobDO> existingJobs = jobMapper.selectList(
                    new LambdaQueryWrapperX<JobDO>()
                        .eq(JobDO::getHandlerName, handlerName)
                );
                
                boolean found = false;
                for (JobDO existingJob : existingJobs) {
                    try {
                        Map<String, Object> existingParam = JsonUtils.parseObject(existingJob.getHandlerParam(), Map.class);
                        Long existingSceneId = Long.valueOf(existingParam.get("sceneId").toString());
                        if (existingSceneId.equals(sceneId)) {
                            found = true;
                            break;
                        }
                    } catch (Exception e) {
                        log.warn("[syncSceneTimer][解析现有任务参数失败，任务ID: {}]", existingJob.getId(), e);
                    }
                }
                
                if (found) {
                    updateSceneTimerInternal(sceneId, condition, false);
                } else {
                    createSceneTimerInternal(sceneId, condition, false);
                }
            }

            log.info("[syncSceneTimer][同步场景定时任务成功，场景ID: {}, 定时条件数量: {}]", 
                    sceneId, timerConditions.size());

        } catch (Exception e) {
            log.error("[syncSceneTimer][同步场景定时任务失败，场景ID: {}]", sceneId, e);
            throw new RuntimeException("同步定时任务失败", e);
        } finally {
            if (clearTenant) {
                // 清理租户上下文
                TenantContextHolder.clear();
            }
        }
    }

    @Override
    public String convertTimeToCron(String timeStr) {
        if (StrUtil.isBlank(timeStr)) {
            throw new IllegalArgumentException("时间字符串不能为空");
        }

        // 解析时间格式 "HH:mm"
        String[] timeParts = timeStr.split(":");
        if (timeParts.length != 2) {
            throw new IllegalArgumentException("时间格式错误，应为 HH:mm 格式");
        }

        int hour = Integer.parseInt(timeParts[0]);
        int minute = Integer.parseInt(timeParts[1]);

        if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
            throw new IllegalArgumentException("时间值超出范围");
        }

        // 转换为Cron表达式：秒 分 时 日 月 周
        // 每天指定时间执行：0 分 时 * * ?
        return String.format("0 %d %d * * ?", minute, hour);
    }

    /**
     * 清理旧的定时任务
     * 删除所有以 "iotSceneTimerJob_" 开头的旧格式任务
     */
    public void cleanupOldJobs() {
        try {
            // 设置默认租户ID
            TenantContextHolder.setTenantId(1L);
            
            log.info("[cleanupOldJobs][开始清理旧的定时任务]");
            
            // 查找所有以 "iotSceneTimerJob_" 开头的任务
            List<JobDO> oldJobs = jobMapper.selectList(
                new LambdaQueryWrapperX<JobDO>()
                    .likeRight(JobDO::getHandlerName, "iotSceneTimerJob_")
            );
            
            if (oldJobs.isEmpty()) {
                log.info("[cleanupOldJobs][没有找到需要清理的旧任务]");
                return;
            }
            
            log.info("[cleanupOldJobs][找到 {} 个旧任务需要清理]", oldJobs.size());
            
            for (JobDO oldJob : oldJobs) {
                try {
                    // 从调度器中删除任务
                    try {
                        schedulerManager.deleteJob(oldJob.getHandlerName());
                    } catch (SchedulerException e) {
                        log.warn("[cleanupOldJobs][从调度器删除旧任务失败，任务名称: {}]", oldJob.getHandlerName(), e);
                    }

                    // 从数据库中删除任务
                    jobMapper.deleteById(oldJob.getId());
                    log.info("[cleanupOldJobs][清理旧任务成功，任务ID: {}, 任务名称: {}]", 
                            oldJob.getId(), oldJob.getHandlerName());
                } catch (Exception e) {
                    log.error("[cleanupOldJobs][清理旧任务失败，任务ID: {}, 任务名称: {}]", 
                            oldJob.getId(), oldJob.getHandlerName(), e);
                }
            }
            
            log.info("[cleanupOldJobs][旧任务清理完成]");
            
        } catch (Exception e) {
            log.error("[cleanupOldJobs][清理旧任务过程中发生异常]", e);
        }
        // 注意：不在这里清理租户上下文，因为调用方需要继续使用
    }
} 